<template>
  <table-page
    ref="tablePageRef"
    title="文章列表"
    :search-config="dynamicSearchConfig"
    :columns="tableColumns"
    :data="processedTableData"
    :loading="loading"
    :pagination-config="paginationConfig"
    :actions-width="210"
    add-title="新增文章"
    edit-title="编辑文章"
    @search="handleSearch"
    @reset="handleSearch"
    @selection-change="handleSelectionChange"
    @size-change="handleSizeChange"
    @current-change="handleCurrentChange"
    @add="handleAdd"
    @edit="handleEdit"
    @delete="handleDelete"
    @batch-delete="handleBatchDelete"
  >
    <!-- 自定义操作按钮 -->
    <template #actions="{ row }">
      <el-button type="primary" link :icon="View" @click="handlePreview(row)"> 预览 </el-button>
      <el-button type="success" link :icon="Download" @click="handleExport(row)"> 导出 </el-button>
      <el-button type="danger" link :icon="Delete" @click="handleDelete(row)"> 删除 </el-button>
    </template>
  </table-page>
</template>

<script setup>
/**
 * 文章列表页面
 *
 * 功能：
 * 1. 文章列表展示与搜索
 * 2. 文章的添加、编辑、删除和批量删除
 * 3. 文章预览和排序功能
 * 4. 列表分页与数据加载
 * 5. 文章推荐功能管理
 */
import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { Edit, View, Delete, Download } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import TablePage from '@/components/TablePage/index.vue'
import { searchConfig, createTableConfig } from './config/pageConfig'
import { articleApi } from '@/api/article'
import { categoryApi } from '@/api/category'

// =============== 状态与配置 ===============

/**
 * 路由和引用
 */
const router = useRouter()
const tablePageRef = ref(null)

/**
 * 数据与加载状态
 */
const loading = ref(false)
const tableData = ref([])
const searchParams = ref({})
const recommendCount = ref(0) // 当前推荐文章数量
const maxRecommendCount = 4 // 最大推荐文章数量

/**
 * 分页配置
 * 控制表格分页行为
 */
const paginationConfig = reactive({
  total: 0, // 总条数
  currentPage: 1, // 当前页码
  pageSize: 10, // 每页条数
  pageSizes: [10, 20, 30, 50], // 可选每页条数
})

// 动态分类选项
const dynamicSearchConfig = ref([])

// =============== 方法 ===============

/**
 * 获取文章列表
 * 根据搜索参数和分页配置获取数据
 */
const fetchArticleList = async () => {
  try {
    loading.value = true
    const params = {
      ...searchParams.value,
      pageSize: paginationConfig.pageSize,
      pageNum: paginationConfig.currentPage,
    }
    const res = await articleApi.getList(params)
    if (res) {
      tableData.value = (res.records || []).map((item) => ({
        ...item,
        tags: Array.isArray(item.tags)
          ? item.tags.map((tag) => (typeof tag === 'string' ? { name: tag } : tag))
          : [],
      }))
      paginationConfig.total = res.total

      // 计算当前推荐文章数量
      recommendCount.value = tableData.value.filter(
        (item) => String(item.isRecommend) === '1',
      ).length

      // 初始化禁用状态
      updateArticlesDisabledState()
    }
  } catch (error) {
    console.error('获取文章列表失败:', error)
    ElMessage.error('获取文章列表失败')
  } finally {
    loading.value = false
  }
}

/**
 * 搜索处理
 * 更新搜索参数并重新加载数据
 * @param {Object} params - 搜索参数
 */
const handleSearch = (params = {}) => {
  searchParams.value = params
  paginationConfig.currentPage = 1
  fetchArticleList()
}

/**
 * 选择变化处理
 * @param {Array} rows - 选中的行数据
 */
const handleSelectionChange = (rows) => {
  console.log('选中行：', rows)
}

/**
 * 分页大小变化
 * @param {number} val - 新的每页条数
 */
const handleSizeChange = (val) => {
  paginationConfig.pageSize = val
  fetchArticleList()
}

/**
 * 页码变化
 * @param {number} val - 新的页码
 */
const handleCurrentChange = (val) => {
  paginationConfig.currentPage = val
  fetchArticleList()
}

/**
 * 新增文章
 * 跳转到文章发布页面
 */
const handleAdd = () => {
  router.push('/article/add')
}

/**
 * 编辑文章
 * 跳转到文章编辑页面
 * @param {Object} row - 文章行数据
 */
const handleEdit = (row) => {
  router.push({
    path: '/article/edit',
    query: { id: row.id },
  })
}

/**
 * 预览文章
 * 跳转到文章预览页面
 * @param {Object} row - 文章行数据
 */
const handlePreview = (row) => {
  router.push({
    name: 'ArticlePreview',
    params: { id: row.id },
  })
}

/**
 * 批量删除
 * 删除多个选中的文章
 * @param {Array} rows - 要删除的文章数组
 */
const handleBatchDelete = async (rows) => {
  try {
    loading.value = true
    const ids = rows.map((row) => row.id)
    await articleApi.delete(ids)
    ElMessage.success('删除成功')
    fetchArticleList()
    tablePageRef.value?.clearSelection()
  } catch (error) {
    console.error('批量删除失败:', error)
    ElMessage.error('批量删除失败')
  } finally {
    loading.value = false
  }
}

/**
 * 单个删除
 * 删除单个文章
 * @param {Object} row - 要删除的文章
 */
const handleDelete = async (row) => {
  try {
    await ElMessageBox.confirm('确定要删除该文章吗？', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    })

    loading.value = true
    await articleApi.delete([row.id])
    ElMessage.success('删除成功')
    fetchArticleList()
  } catch (error) {
    if (error !== 'cancel') {
      console.error('删除失败:', error)
      ElMessage.error('删除失败')
    }
  } finally {
    loading.value = false
  }
}

/**
 * 处理上移
 * 调整文章排序顺序
 * @param {Object} row - 要调整的文章
 */
const handleMoveUp = async (row) => {
  const index = tableData.value.findIndex((item) => item.id === row.id)
  if (index <= 0) return

  try {
    loading.value = true
    const newData = JSON.parse(JSON.stringify(tableData.value))

    const currentOrder = newData[index].orderNum
    const targetOrder = newData[index - 1].orderNum

    // 如果当前序号已经是0，则不允许上移
    if (currentOrder === 0) return

    if (currentOrder === targetOrder) {
      // 序号相同时，减小当前序号，但不能小于0
      const newOrder = Math.max(currentOrder - 1, 0)
      await articleApi.updateOrder(row.id, newOrder)
    } else {
      // 序号不同时，交换序号
      await articleApi.updateOrder(row.id, targetOrder)
      await articleApi.updateOrder(newData[index - 1].id, currentOrder)
    }

    // 刷新列表
    await fetchArticleList()
    ElMessage.success('移动成功')
  } catch (error) {
    console.error('移动失败:', error)
    ElMessage.error('移动失败')
  } finally {
    loading.value = false
  }
}

/**
 * 处理下移
 * 调整文章排序顺序
 * @param {Object} row - 要调整的文章
 */
const handleMoveDown = async (row) => {
  const index = tableData.value.findIndex((item) => item.id === row.id)
  if (index >= tableData.value.length - 1) return

  try {
    loading.value = true
    const newData = JSON.parse(JSON.stringify(tableData.value))

    const currentOrder = newData[index].orderNum
    const targetOrder = newData[index + 1].orderNum

    if (currentOrder === targetOrder) {
      // 序号相同时，增加当前序号
      await articleApi.updateOrder(row.id, currentOrder + 1)
    } else {
      // 序号不同时，交换序号
      await articleApi.updateOrder(row.id, targetOrder)
      await articleApi.updateOrder(newData[index + 1].id, currentOrder)
    }

    // 刷新列表
    await fetchArticleList()
    ElMessage.success('移动成功')
  } catch (error) {
    console.error('移动失败:', error)
    ElMessage.error('移动失败')
  } finally {
    loading.value = false
  }
}

/**
 * 导出文章为Markdown
 * @param {Object} row - 要导出的文章
 */
const handleExport = async (row) => {
  try {
    loading.value = true
    const response = await articleApi.exportMarkdown(row.id)

    // 检查响应类型，如果是JSON则说明是错误信息
    if (response.type === 'application/json') {
      const reader = new FileReader()
      reader.onload = () => {
        const error = JSON.parse(reader.result)
        ElMessage.error(error.message || '导出失败')
      }
      reader.readAsText(response)
      return
    }

    // 创建下载链接
    const url = window.URL.createObjectURL(response)
    const link = document.createElement('a')
    link.href = url
    link.download = `${row.title}.md` // 使用文章标题作为文件名

    // 触发下载
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(url)

    ElMessage.success('导出成功')
  } catch (error) {
    console.error('导出失败:', error)
    ElMessage.error('导出失败')
  } finally {
    loading.value = false
  }
}

/**
 * 切换文章推荐状态
 * @param {Object} row - 文章行数据
 * @param {Boolean} value - 新的推荐状态
 */
const handleToggleRecommend = async (row, value) => {
  // 如果要设置为推荐状态，但没有封面图片，则提示错误并拦截请求
  if (value && (!row.picUrl || row.picUrl.trim() === '')) {
    ElMessage.warning('只有设置了封面图片的文章才能被推荐')
    return
  }

  // 如果当前已有4个推荐文章且要设置为推荐，则提示错误
  if (value && recommendCount.value >= maxRecommendCount && row.isRecommend !== '1') {
    ElMessage.warning(`最多只能推荐${maxRecommendCount}篇文章`)
    return
  }

  try {
    loading.value = true
    const newRecommendValue = value ? '1' : '0'
    await articleApi.updateRecommend(row.id, newRecommendValue)

    // 1. 先更新当前行的推荐状态
    const updatedRow = { ...row, isRecommend: newRecommendValue }

    // 2. 更新tableData中对应的行
    const index = tableData.value.findIndex((item) => item.id === row.id)
    if (index !== -1) {
      tableData.value = [
        ...tableData.value.slice(0, index),
        updatedRow,
        ...tableData.value.slice(index + 1),
      ]
    }

    // 3. 重新计算推荐数量
    recommendCount.value = tableData.value.filter((item) => String(item.isRecommend) === '1').length

    // 4. 更新所有文章的禁用状态
    updateArticlesDisabledState()

    ElMessage.success(value ? '已设为推荐' : '已取消推荐')
  } catch (error) {
    console.error('更新推荐状态失败:', error)
  } finally {
    loading.value = false
  }
}

/**
 * 更新所有文章的禁用状态
 * 当推荐数量达到上限时，禁用未推荐文章的推荐开关
 * 对于无封面图片的文章，始终禁用推荐开关
 */
const updateArticlesDisabledState = () => {
  // 计算最新的推荐数量
  const currentRecommendCount = tableData.value.filter(
    (item) => String(item.isRecommend) === '1',
  ).length

  // 创建新数组以触发Vue的响应式更新
  tableData.value = tableData.value.map((item) => {
    // 禁用条件：1. 无封面图片 或 2. 推荐数量已达上限且当前不是推荐状态
    const noCover = !item.picUrl || item.picUrl.trim() === ''
    const reachedLimit =
      currentRecommendCount >= maxRecommendCount && String(item.isRecommend) !== '1'

    return {
      ...item,
      recommendDisabled: noCover || reachedLimit,
    }
  })
}

/**
 * 获取所有分类并注入到搜索配置
 */
const fetchCategoriesForSearch = async () => {
  const res = await categoryApi.getAll()
  const data = Array.isArray(res) ? res : res?.data
  if (Array.isArray(data)) {
    const categoryOptions = data.map((item) => ({ label: item.name, value: item.id }))
    dynamicSearchConfig.value = searchConfig.map((cfg) => {
      if (cfg.field === 'categoryId') {
        return { ...cfg, options: categoryOptions }
      }
      return cfg
    })
  } else {
    dynamicSearchConfig.value = searchConfig
  }
}

// =============== 计算属性 ===============

/**
 * 处理后的表格数据
 * 为每一行添加排序方法和按钮状态
 */
const processedTableData = computed(() => {
  return tableData.value.map((item) => {
    // 确保 isRecommend 是字符串类型
    const isRecommend = String(item.isRecommend || '0')

    return {
      ...item,
      isRecommend,
      onMoveUp: handleMoveUp,
      onMoveDown: handleMoveDown,
      upDisabled: item.orderNum === 0,
      onToggleRecommend: handleToggleRecommend,
      // 使用item中的recommendDisabled属性，如果不存在则计算
      recommendDisabled:
        item.recommendDisabled !== undefined
          ? item.recommendDisabled
          : recommendCount.value >= maxRecommendCount && isRecommend !== '1',
    }
  })
})

/**
 * 表格列配置
 * 从配置文件中获取并注入必要的处理函数
 */
const tableColumns = computed(() =>
  createTableConfig(router, {
    onMoveUp: handleMoveUp,
    onMoveDown: handleMoveDown,
  }),
)

// =============== 生命周期 ===============

/**
 * 页面加载时获取数据
 */
onMounted(() => {
  fetchCategoriesForSearch()
  fetchArticleList()
})
</script>

<style lang="scss" scoped>
// 导入主题变量和混合器
@use '@/assets/styles/theme/base.scss' as *;
@use '@/assets/styles/mixins.scss' as *;

/**
 * 文章列表页样式
 */

/**
 * 多行文本省略的混合器
 */
@mixin multi-text-ellipsis($lines) {
  display: -webkit-box;
  -webkit-line-clamp: $lines;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

/**
 * 文章标题样式
 */
:deep(.article-title) {
  font-weight: 500;
  color: $color-primary;
  @include transition(color);
  padding: 8px 0;

  &:hover {
    color: lighten($color-primary, 10%);
  }
}

/**
 * 封面列样式
 */
:deep(.cover-column) {
  position: relative;
  width: 70px;
  height: 50px;
  margin: 0 auto;
  border-radius: $radius-base;
  overflow: hidden;
  cursor: pointer;

  .article-cover {
    width: 100%;
    height: 100%;
    object-fit: cover;
    @include transition(all);
  }

  .preview-overlay {
    position: absolute;
    inset: 0;
    background-color: rgba(0, 0, 0, 0.3);
    @include flex-center;
    opacity: 0;
    @include transition(opacity);

    .preview-icon {
      color: white;
      font-size: 20px;
      filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3));
    }
  }

  &:hover {
    .article-cover {
      transform: scale(1.1);
    }

    .preview-overlay {
      opacity: 1;
    }
  }
}

/**
 * 无封面样式
 */
:deep(.no-cover) {
  height: 50px;
  @include flex-center;
  color: $text-secondary;
  font-size: $font-size-small;
  background-color: $bg-light;
  border-radius: $radius-base;
}

// 操作列样式
.operation-cell {
  .cell {
    padding: 0 4px;

    .operation-buttons {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 4px;

      .el-button {
        height: 28px;
        padding: 0 6px;
        font-size: 12px;

        &.el-button--text {
          margin: 0;

          .el-icon {
            font-size: 14px;
          }
        }

        :deep(.el-button__content) {
          display: inline-flex;
          align-items: center;
          gap: 2px;
          white-space: nowrap;
        }
      }
    }
  }
}
</style>
